using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;

namespace PathFinding
{
    /// <summary>
    ///   <para>A world position that is guaranteed to be on the surface of the NavMesh.</para>
    /// </summary>
    public struct PathLocation
    {
        public PathPolygonId polygon { get; }

        /// <summary>
        ///   <para>A world position that sits precisely on the surface of the NavMesh or along its links.</para>
        /// </summary>
        public Vector3 position { get; }
    }

    public struct AgentBody : IComponentData
    {
        /// <summary>
        /// Force that agent will use to move towards. Calculated from agent position and destination.
        /// </summary>
        public Vector3 Force;

        /// <summary>
        /// The velocity agent is moving.
        /// </summary>
        public Vector3 Velocity;

        /// <summary>
        /// Final destination that agent will attempt to reach.
        /// </summary>
        public Vector3 Destination;

        /// <summary>
        /// The distance between the agent's position and the destination.
        /// With NavMesh this value will return partial remaining distance not full path.
        /// </summary>
        public float RemainingDistance;

        /// <summary>
        /// This property holds the stop or resume condition of the agent.
        /// </summary>
        public bool IsStopped;

        /// <summary>
        /// Returns the legnth of <see cref="Velocity"/>.
        /// </summary>
        public float Speed => PathMath.Length(Velocity);

        /// <summary>
        /// Returns default configuration.
        /// </summary>
        public static AgentBody Default => new()
        {
            IsStopped = true,
        };

        /// <summary>
        /// Sets properties for new agent destination.
        /// </summary>
        public void SetDestination(Vector3 value)
        {
            Destination = value;
            IsStopped = false;
        }

        /// <summary>
        /// Sets properties for agent to stop.
        /// </summary>
        public void Stop()
        {
            IsStopped = true;
            Velocity = Vector3.zero;
        }
    }

    /// <summary>
    /// Agent's locomotion that moves towards destination with arrival.
    /// </summary>
    public struct AgentLocomotion : IComponentData
    {
        /// <summary>
        /// Maximum movement speed when moving to destination.
        /// </summary>
        public float Speed;

        /// <summary>
        /// The maximum acceleration of an agent as it follows a path, given in units / sec^2.
        /// </summary>
        public float Acceleration;

        /// <summary>
        /// Maximum turning speed in (rad/s) while following a path.
        /// </summary>
        public float AngularSpeed;

        /// <summary>
        /// Stop within this distance from the target position.
        /// </summary>
        public float StoppingDistance;

        /// <summary>
        /// Should the agent brake automatically to avoid overshooting the destination point?
        /// </summary>
        public bool AutoBreaking;

        /// <summary>
        /// Returns default configuration.
        /// </summary>
        public static AgentLocomotion Default => new()
        {
            Speed = 3.5f,
            Acceleration = 8,
            AngularSpeed = math.radians(120),
            StoppingDistance = 0.1f,
            AutoBreaking = true,
        };
    }

    /// <summary>
    /// Agent tag.
    /// </summary>
    public struct Agent : IComponentData
    {
        public NavigationLayers Layers;

        /// <summary>
        /// Returns default configuration.
        /// </summary>
        public static Agent Default => new() { Layers = NavigationLayers.Default };
    }

    public enum ShapeType
    {
        Circle,
        Cylinder,
    }

    /// <summary>
    /// Represents agent shape in space.
    /// In the future this component might be split into specific shape components instead of having one big generic.
    /// </summary>
    public struct AgentShape : IComponentData
    {
        static quaternion CylinderRotation = quaternion.identity;
        static quaternion CircleRotation = UnityEngine.Quaternion.Euler(-90, 0, 0);

        /// <summary>
        /// The radius of the agent.
        /// </summary>
        public float Radius;

        /// <summary>
        /// The height of the agent for purposes of passing under obstacles, etc.
        /// </summary>
        public float Height;

        /// <summary>
        /// The type of shape.
        /// </summary>
        public ShapeType Type;

        public float3 Up => Type == ShapeType.Cylinder ? new float3(0, 1, 0) : new float3(0, 0, 1);
        public quaternion Orentation => Type == ShapeType.Cylinder ? CylinderRotation : CircleRotation;

        /// <summary>
        /// Returns default configuration.
        /// </summary>
        public static AgentShape Default => new()
        {
            Radius = 0.5f,
            Height = 2,
            Type = ShapeType.Cylinder,
        };

        /// <summary>
        /// Returns up vector of shape.
        /// </summary>
        /// <returns>Returns up vector of shape.</returns>
        public float3 GetUp()
        {
            if (Type == ShapeType.Cylinder)
                return new float3(0, 1, 0);
            else
                return new float3(0, 0, 1);
        }
    }
    
    [System.Flags]
    public enum NavigationLayers
    {
        None = 0,
        Default = 1 << 0,
        Layer1 = 1 << 1,
        Layer2 = 1 << 2,
        Layer3 = 1 << 3,
        Layer4 = 1 << 4,
        Layer5 = 1 << 5,
        Layer6 = 1 << 6,
        Layer7 = 1 << 7,
        Layer8 = 1 << 8,
        Layer9 = 1 << 9,
        Layer10 = 1 << 10,
        Layer11 = 1 << 11,
        Layer12 = 1 << 12,
        Layer13 = 1 << 13,
        Layer14 = 1 << 14,
        Layer15 = 1 << 15,
        Layer16 = 1 << 16,
        Layer17 = 1 << 17,
        Layer18 = 1 << 18,
        Layer19 = 1 << 19,
        Layer20 = 1 << 20,
        Layer21 = 1 << 21,
        Layer22 = 1 << 22,
        Layer23 = 1 << 23,
        Layer24 = 1 << 24,
        Layer25 = 1 << 25,
        Layer26 = 1 << 26,
        Layer27 = 1 << 27,
        Layer28 = 1 << 28,
        Layer29 = 1 << 29,
        Layer30 = 1 << 30,
        Layer31 = 1 << 31,
        Everything = -1,
    }
}